summaryrefslogtreecommitdiffhomepage
path: root/packages/console/app/src/routes/workspace/[id]/settings/settings-section.tsx
blob: a9950c41e4df3064ab6f6605e94e03d5d2e6071d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import { json, action, useParams, useSubmission, createAsync, query } from "@solidjs/router"
import { createEffect, Show } from "solid-js"
import { createStore } from "solid-js/store"
import { withActor } from "~/context/auth.withActor"
import { Workspace } from "@opencode-ai/console-core/workspace.js"
import styles from "./settings-section.module.css"
import { Database, eq } from "@opencode-ai/console-core/drizzle/index.js"
import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js"
import { useI18n } from "~/context/i18n"
import { formError, localizeError } from "~/lib/form-error"

const getWorkspaceInfo = query(async (workspaceID: string) => {
  "use server"
  return withActor(
    () =>
      Database.use((tx) =>
        tx
          .select({
            id: WorkspaceTable.id,
            name: WorkspaceTable.name,
            slug: WorkspaceTable.slug,
          })
          .from(WorkspaceTable)
          .where(eq(WorkspaceTable.id, workspaceID))
          .then((rows) => rows[0] || null),
      ),
    workspaceID,
  )
}, "workspace.get")

const updateWorkspace = action(async (form: FormData) => {
  "use server"
  const name = (form.get("name") as string | null)?.trim()
  if (!name) return { error: formError.workspaceNameRequired }
  if (name.length > 255) return { error: formError.nameTooLong }
  const workspaceID = form.get("workspaceID") as string | null
  if (!workspaceID) return { error: formError.workspaceRequired }
  return json(
    await withActor(
      () =>
        Workspace.update({ name })
          .then(() => ({ error: undefined }))
          .catch((e) => ({ error: e.message as string })),
      workspaceID,
    ),
  )
}, "workspace.update")

export function SettingsSection() {
  const params = useParams()
  const i18n = useI18n()
  const workspaceInfo = createAsync(() => getWorkspaceInfo(params.id!))
  const submission = useSubmission(updateWorkspace)
  const [store, setStore] = createStore({ show: false })

  let input: HTMLInputElement

  createEffect(() => {
    if (!submission.pending && submission.result && !submission.result.error) {
      hide()
    }
  })

  function show() {
    while (true) {
      submission.clear()
      if (!submission.result) break
    }
    setStore("show", true)
    input.focus()
  }

  function hide() {
    setStore("show", false)
  }

  return (
    <section class={styles.root}>
      <div data-slot="section-title">
        <h2>{i18n.t("workspace.settings.title")}</h2>
        <p>{i18n.t("workspace.settings.subtitle")}</p>
      </div>
      <div data-slot="section-content">
        <div data-slot="setting">
          <p>{i18n.t("workspace.settings.workspaceName")}</p>
          <Show
            when={!store.show}
            fallback={
              <form action={updateWorkspace} method="post" data-slot="create-form">
                <div data-slot="input-container">
                  <input
                    required
                    ref={(r) => (input = r)}
                    data-component="input"
                    name="name"
                    type="text"
                    placeholder={i18n.t("workspace.settings.workspaceName")}
                    value={workspaceInfo()?.name ?? i18n.t("workspace.settings.defaultName")}
                  />
                  <input type="hidden" name="workspaceID" value={params.id} />
                  <button type="submit" data-color="primary" disabled={submission.pending}>
                    {submission.pending ? i18n.t("workspace.settings.updating") : i18n.t("workspace.settings.save")}
                  </button>
                  <button type="reset" data-color="ghost" onClick={() => hide()}>
                    {i18n.t("common.cancel")}
                  </button>
                </div>
                <Show when={submission.result && submission.result.error}>
                  {(err) => <div data-slot="form-error">{localizeError(i18n.t, err())}</div>}
                </Show>
              </form>
            }
          >
            <div data-slot="value-with-action">
              <p data-slot="current-value">{workspaceInfo()?.name}</p>
              <button data-color="primary" onClick={() => show()}>
                {i18n.t("workspace.settings.edit")}
              </button>
            </div>
          </Show>
        </div>
      </div>
    </section>
  )
}